package com.hz.house.service.home;

import com.alibaba.fastjson.JSON;
import com.hz.house.bean.entity.BuildingDiscount;
import com.hz.house.bean.entity.BuildingFocusReqBean;
import com.hz.house.bean.es.BuildingSearchDocumentBean;
import com.hz.house.bean.es.TagSearchBean;
import com.hz.house.bean.vo.*;
import com.hz.house.cache.HomePageCacheService;
import com.hz.house.common.Constants;
import com.hz.house.common.ResultUtil;
import com.hz.house.dao.BuildingDetailMapper;
import com.hz.house.enums.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.NestedSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;

import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 楼盘搜索
 *
 * @author lfw
 */
@Service
@Slf4j
public class BuildingSearchService {

    @Autowired
    private ElasticsearchTemplate esTemplate;

    @Autowired
    private BuildingDetailMapper detailMapper;

    @Autowired
    private HomePageCacheService cacheService;

    //一张默认的楼盘封面图片地址
    private static final String deaultCoverImg = "https://housessmall-dev-index-img-1258778894."
            + "cos.ap-chengdu.myqcloud.com/icon/nodata_index.png";

    /**
     * 普通的楼盘搜索，非标签搜索
     *
     * @param condition
     * @return
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public Object generalSearch(BuildingSearchConditionBean condition) {

        System.out.println(JSON.toJSONString(condition));
        if (condition.getPage() == null) {
            condition.setPage(-1);//-1表示不分页
        }
        BoolQueryBuilder metaQuery = QueryBuilders.boolQuery();
        //处理关键字输入查询
        if (!StringUtils.isEmpty(condition.getKeyword())) {
            return handleKeyword(metaQuery, condition);
        }
        //处理普通筛选查询，则不处理高光
        condition.setHighLight(false);

        //处理5大类分类查询
        handle5Type(condition, metaQuery);
        //处理区域
        handleRegion(condition, metaQuery);
        //处理地铁
        handleSubway(condition, metaQuery);
        //处理总价
        handleTotalPrice(condition, metaQuery);
        //处理单价
        handlePerPrice(condition, metaQuery);
        //处理面积
        handleSpace(condition, metaQuery);
        //处理户型
        handleHouseModel(condition, metaQuery);
        //处理物业类型
        handlePropType(condition, metaQuery);
        //处理装修
        handleDecoType(condition, metaQuery);
        //处理售卖状态
        handleSellStatus(condition, metaQuery);
        //处理排序
        List<SortBuilder> sort = handleSort(condition);
        //获取要返回的字段
        String[] fileds = getFileds(false);
        //执行查询
        Map<String, Object> data = doSearchBuildingOrderList(metaQuery, condition.getPage(), sort, fileds, false);
        //处理数据结果
        List<Map<String, Object>> res = (List<Map<String, Object>>) data.get("data");
        long total = (long) data.get("total");
        List<BuildingSearchDocumentBean> rex = new ArrayList<>();
        res.forEach(d -> {
            //将map转换为对象进行数据处理
            rex.add(JSON.parseObject(JSON.toJSONString(d), BuildingSearchDocumentBean.class));
        });
        Map<String, Object> m = new HashMap<>();
        m.put("total", total);
        //处理数据并封装返回
        m.put("rows", handleResultData(rex));
        return ResultUtil.ok(m);
    }

    //处理排序
    @SuppressWarnings("rawtypes")
    private List<SortBuilder> handleSort(BuildingSearchConditionBean condition) {
        List<SortBuilder> sorts = new ArrayList<SortBuilder>();
        //处理用户选择了排序
        if (condition.getSortId() != null && condition.getSortId() != 0) {
            String filed = "";
            switch (condition.getSortId()) {
                case 1:
                case 2:
                    filed = "kps.minPerPrice";
                    break;
                case 3:
                case 4:
                    filed = "kps.minTotalPrice";
                    break;
                case 5:
                case 6:
                    filed = "kps.minSpace";
                    break;
            }
            if (filed.length() > 0) {
                SortOrder order = condition.getSortId() % 2 == 0 ? SortOrder.DESC : SortOrder.ASC;
                NestedSortBuilder nsb = new NestedSortBuilder("kps");
                SortBuilder sort = SortBuilders.fieldSort(filed).order(order).setNestedSort(nsb);
                sorts.add(sort);
                return sorts;
            }
        }
        //用户没有选择的情况下，根据所在楼盘类型或置顶数据来排序
        String type = condition.getBuildingType();
        if (!StringUtils.isEmpty(type)) {
            String path = "";
            String field = "";
            SimpleDateFormat sdfs = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
            long now = 0l;
            try {
                now = sdfs.parse(sdf.format(new Date())).getTime();
            } catch (ParseException e) {
            }
            QueryBuilder filter = null;//使用filter指定排序字段的数据是哪一条
            if (type.equals(Constants.HOME_PAGE_BUILDING_TYPE_RMLP)
                    || type.equals(Constants.HOME_PAGE_BUILDING_TYPE_JJQZ)
                    || type.equals(Constants.HOME_PAGE_BUILDING_TYPE_YHSY)) {
                path = "tops";
                field = "tops.createTime";
                filter = QueryBuilders.termQuery("tops.topType", type);
                NestedSortBuilder nsb = new NestedSortBuilder(path);
                nsb.setFilter(filter);
                SortBuilder sort = SortBuilders.fieldSort(field).order(SortOrder.DESC).setNestedSort(nsb);
                sorts.add(sort);
            } else if (type.equals(Constants.HOME_PAGE_BUILDING_TYPE_ZXQZ)) {
                path = "kps";
                field = "kps.preSellTime";
                //过滤掉非当前开盘，有可能没有结束选房时间
                BoolQueryBuilder kpsort = QueryBuilders.boolQuery();
                BoolQueryBuilder kpsf = QueryBuilders.boolQuery();
                kpsf.must(QueryBuilders.rangeQuery("kps.endSelect").gte(now));
                kpsf.must(QueryBuilders.rangeQuery("kps.preSellTime").lte(now));
                kpsort.should(kpsf);
                BoolQueryBuilder kpsx = QueryBuilders.boolQuery();
                kpsx.must(QueryBuilders.rangeQuery("kps.preSellTime").gte(now - (12 * 24 * 60 * 60 * 1000L)));
                kpsx.must(QueryBuilders.rangeQuery("kps.preSellTime").lte(now));
                kpsort.should(kpsx);
                NestedSortBuilder nsb = new NestedSortBuilder(path);
                nsb.setFilter(kpsort);
                SortBuilder sort = SortBuilders.fieldSort(field).order(SortOrder.DESC).setNestedSort(nsb);
                sorts.add(sort);
            } else {
                //最新摇号  开盘时间必须在预售时间和结束选房时间之间
                //或者 在预售时间+12天之内  如果没有摇号时间  按照预售时间降序排列
                BoolQueryBuilder kpsort = QueryBuilders.boolQuery();
                BoolQueryBuilder kpsf = QueryBuilders.boolQuery();
                kpsf.must(QueryBuilders.rangeQuery("kps.endSelect").gte(now));
                kpsf.must(QueryBuilders.rangeQuery("kps.preSellTime").lte(now));
                kpsort.should(kpsf);
                BoolQueryBuilder kpsx = QueryBuilders.boolQuery();
                kpsx.must(QueryBuilders.rangeQuery("kps.preSellTime").gte(now - (12 * 24 * 60 * 60 * 1000L)));
                kpsx.must(QueryBuilders.rangeQuery("kps.preSellTime").lte(now));
                kpsort.should(kpsx);
                NestedSortBuilder nsb = new NestedSortBuilder("kps");
                nsb.setFilter(kpsort);
                SortBuilder sort = SortBuilders.fieldSort("kps.yaohao").order(SortOrder.DESC).setNestedSort(nsb);
                sorts.add(sort);
                //没有摇号时间的情况下
                NestedSortBuilder nsbx = new NestedSortBuilder("kps");
                nsb.setFilter(kpsort);
                SortBuilder sortx = SortBuilders.fieldSort("kps.preSellTime").order(SortOrder.DESC).setNestedSort(nsbx);
                sorts.add(sortx);
            }

            return sorts;
        }
        //没有类型的情况使用默认排序
        SortBuilder sort = SortBuilders.fieldSort("rank").order(SortOrder.ASC);
        sorts.add(sort);
        return sorts;
    }


    private void handleSellStatus(BuildingSearchConditionBean condition, BoolQueryBuilder metaQuery) {

        if (condition.getSellStatusId() == null || condition.getSellStatusId() == 0) {
            return;
        }
        int stat = condition.getSellStatusId();
        if (stat == 1 || stat == 2 || stat == 10) {
            metaQuery.must(QueryBuilders.termQuery("sellStatus", stat));
        } else {
            metaQuery.must(QueryBuilders.termsQuery("sellStatus", new int[]{3, 4, 5, 6, 7, 8, 9}));
        }
    }


    //装修类型在开盘信息里
    private void handleDecoType(BuildingSearchConditionBean condition, BoolQueryBuilder metaQuery) {
        if (condition.getDecoTypeId() == null)
            return;
        SearchDecorateConditionEnum sdc = SearchDecorateConditionEnum.getSearchDecoById(condition.getDecoTypeId());
        if (sdc == null)
            return;
        String type = sdc.getType();
        String mix = sdc.getMix();
        BoolQueryBuilder deco = QueryBuilders.boolQuery();
        deco.should(QueryBuilders.termQuery("kps.decoType", type));
        deco.should(QueryBuilders.termQuery("kps.decoType", mix));
        NestedQueryBuilder nest = QueryBuilders.nestedQuery("kps", deco, ScoreMode.None);
        metaQuery.must(nest);
    }

    private void handlePropType(BuildingSearchConditionBean condition, BoolQueryBuilder metaQuery) {

        if (condition.getPropTypeId() == null)
            return;
        SearchPropTypeConditionEnum spt = SearchPropTypeConditionEnum.getSearchPropTypeById(condition.getPropTypeId());
        if (spt == null)
            return;
        //他妈的，忘了物业类型字段。只有从标签库里查了
        int tagId = spt.getTagId();

        if (tagId == -1) {//住宅不管了
            return;
        }
        QueryBuilder tagq = QueryBuilders.termQuery("tags.tagId", spt.getTagId());
        NestedQueryBuilder ns = QueryBuilders.nestedQuery("tags", tagq, ScoreMode.None);
        metaQuery.must(ns);
    }


    private void handleHouseModel(BuildingSearchConditionBean condition, BoolQueryBuilder metaQuery) {

        if (condition.getHouseModelId() == null)
            return;

        SearchHouseModelConditionEnum sm = SearchHouseModelConditionEnum.getHouseModelById(condition.getHouseModelId());
        if (sm == null)
            return;
        String type = sm.getType();
        metaQuery.must(QueryBuilders.matchQuery("houseModel", type));
    }

    private void handleSpace(BuildingSearchConditionBean condition, BoolQueryBuilder metaQuery) {
        if (condition.getSpaceId() == null)
            return;
        SearchSpaceConditionEnum sc = SearchSpaceConditionEnum.getSpaceConditionByid(condition.getSpaceId());
        if (sc == null)
            return;
        float min = sc.getMinSpace();
        float max = sc.getMaxSpace();
        System.out.println("min-space:" + min + ",max-space:" + max);
        BoolQueryBuilder space = QueryBuilders.boolQuery();
        QueryBuilder spmax = QueryBuilders.rangeQuery("kps.minSpace").lte(max);
        QueryBuilder spmin = QueryBuilders.rangeQuery("kps.maxSpace").gte(min);
        NestedQueryBuilder nested = QueryBuilders.nestedQuery("kps", space, ScoreMode.None);
        space.must(spmax);
        space.must(spmin);
        metaQuery.must(nested);
    }


    private void handlePerPrice(BuildingSearchConditionBean condition, BoolQueryBuilder metaQuery) {
        BoolQueryBuilder perPrice = QueryBuilders.boolQuery();
        boolean isHandled = false;
        //输入优先  都以开盘的最高单价为参考查询
        if (condition.getMinPerPrice() != null && condition.getMinPerPrice() > 0) {
            //最低价大于等于开盘单价最低价
            QueryBuilder ppmin = QueryBuilders.rangeQuery("kps.maxPerPrice").gte(condition.getMinPerPrice());
            perPrice.must(ppmin);
            isHandled = true;
        }
        if (condition.getMaxPerPrice() != null && condition.getMaxPerPrice() > 0) {
            //最高价大于等于开盘单价最低价
            QueryBuilder ppmax = QueryBuilders.rangeQuery("kps.minPerPrice").lte(condition.getMaxPerPrice());
            perPrice.must(ppmax);
            isHandled = true;
        }
        if (condition.getMinPerPrice() == null && condition.getMaxPerPrice() == null) {
            //处理选择的
            if (condition.getPerPriceId() != null && condition.getPerPriceId() != -1) {
                SearchPerPriceConditionEnum per = SearchPerPriceConditionEnum
                        .getSearchPerPriceConditionById(condition.getPerPriceId());
                if (per != null) {
                    int max = per.getMax();
                    int min = per.getMin();
                    QueryBuilder ppmax = QueryBuilders.rangeQuery("kps.minPerPrice").lte(max);
                    QueryBuilder ppmin = QueryBuilders.rangeQuery("kps.maxPerPrice").gte(min);
                    perPrice.must(ppmax);
                    perPrice.must(ppmin);
                    isHandled = true;
                }

            }
        }
        //只有处理了才加入查询体
        if (isHandled) {
            QueryBuilder nested = QueryBuilders.nestedQuery("kps", perPrice, ScoreMode.None);
            metaQuery.must(nested);
        }
    }


    //总价要处理 选择和输入的区别
    private void handleTotalPrice(BuildingSearchConditionBean condition, BoolQueryBuilder metaQuery) {

        BoolQueryBuilder totalPrice = QueryBuilders.boolQuery();
        boolean isHandled = false;
        //输入优先
        if (condition.getMinTotalPrice() != null && condition.getMinTotalPrice() > 0) {
            //最低价大于等于开盘总价最低价
            QueryBuilder tpmin = QueryBuilders.rangeQuery("kps.maxTotalPrice").gte(condition.getMinTotalPrice());
            totalPrice.must(tpmin);
            isHandled = true;
        }
        if (condition.getMaxTotalPrice() != null && condition.getMaxTotalPrice() > 0) {
            //最高价也是大于等于开盘总价最低价
            QueryBuilder tpmax = QueryBuilders.rangeQuery("kps.minTotalPrice").lte(condition.getMinTotalPrice());
            totalPrice.must(tpmax);
            isHandled = true;
        }
        if (condition.getMinTotalPrice() == null && condition.getMaxTotalPrice() == null) {
            //处理选择的
            if (condition.getTotalPriceId() != null && condition.getTotalPriceId() != -1) {
                SearchTotalPriceConditionEnum total = SearchTotalPriceConditionEnum
                        .getSearchTotalPriceConditionById(condition.getTotalPriceId());
                if (total != null) {
                    //最高价也是大于等于开盘总价最低价
                    int max = total.getMax();
                    int min = total.getMin();
                    QueryBuilder tpmax = QueryBuilders.rangeQuery("kps.minTotalPrice").lte(max);
                    QueryBuilder tpmin = QueryBuilders.rangeQuery("kps.maxTotalPrice").gte(min);
                    totalPrice.must(tpmax);
                    totalPrice.must(tpmin);
                    isHandled = true;
                }
            }
        }
        //只有处理了才加入查询体
        if (isHandled) {
            QueryBuilder nested = QueryBuilders.nestedQuery("kps", totalPrice, ScoreMode.None);
            metaQuery.must(nested);
        }

    }


    private void handleSubway(BuildingSearchConditionBean condition, BoolQueryBuilder metaQuery) {
        if (StringUtils.isEmpty(condition.getSubwayIds()))
            return;
        //打散地铁id
        String[] ids = condition.getSubwayIds().split(",");
        int[] subids = new int[ids.length];
        for (int i = 0; i < ids.length; i++) {
            try {
                subids[i] = Integer.parseInt(ids[i]);
            } catch (Exception e) {
                log.error("处理地铁id转换错误", e);
            }
        }
        //这个是嵌套 terms查询
        BoolQueryBuilder bool = QueryBuilders.boolQuery();
        bool.must(QueryBuilders.termsQuery("zbs.lineCode", subids));
        QueryBuilder range = QueryBuilders.rangeQuery("zbs.distance").lte(1000);
        bool.must(range);
        QueryBuilder nested = QueryBuilders.nestedQuery("zbs", bool, ScoreMode.None);
        metaQuery.must(nested);
    }


    //处理选择的区域，可以多选的
    private void handleRegion(BuildingSearchConditionBean condition, BoolQueryBuilder metaQuery) {
        if (StringUtils.isEmpty(condition.getRegionIds()))
            return;
        //打散区域id
        String[] ids = condition.getRegionIds().split(",");
        int[] regionIds = new int[ids.length];
        for (int i = 0; i < ids.length; i++) {
            try {
                regionIds[i] = Integer.parseInt(ids[i]);
            } catch (Exception e) {
                log.error("处理区域id转换错误", e);
            }
        }
        metaQuery.must(QueryBuilders.termsQuery("regionId", regionIds));
    }


    //处理首页楼盘5大类分类
    private void handle5Type(BuildingSearchConditionBean condition, BoolQueryBuilder metaQuery) {
        //未选择类型，即为全部楼盘
        if (StringUtils.isEmpty(condition.getBuildingType())) {
            return;
        }
        //热门楼盘
        if (condition.getBuildingType().equals(Constants.HOME_PAGE_BUILDING_TYPE_RMLP)) {
            metaQuery.must(QueryBuilders.wildcardQuery("type", "*" + BuildingGroupEnum.hotBuild.getCode() + "*"));
        }
        //即将取证
        if (condition.getBuildingType().equals(Constants.HOME_PAGE_BUILDING_TYPE_JJQZ)) {
            metaQuery.must(QueryBuilders.wildcardQuery("type", "*" + BuildingGroupEnum.notCertification.getCode() + "*"));
        }
        //最新取证,比较特殊
        if (condition.getBuildingType().equals(Constants.HOME_PAGE_BUILDING_TYPE_ZXQZ)) {
            metaQuery.must(QueryBuilders.wildcardQuery("type", "*" + BuildingGroupEnum.newCertification.getCode() + "*"));
        }
        //最新摇号 满足678且5的时候当前时间大于开始公示时间
        if (condition.getBuildingType().equals(Constants.HOME_PAGE_BUILDING_TYPE_ZXYH)) {
            metaQuery.must(QueryBuilders.wildcardQuery("type", "*" + BuildingGroupEnum.newShakeNumber.getCode() + "*"));
        }

        //摇号剩余  剩余房源大于20套的任何状态下的楼盘
        if (condition.getBuildingType().equals(Constants.HOME_PAGE_BUILDING_TYPE_YHSY)) {
            BoolQueryBuilder yhsy = QueryBuilders.boolQuery();
            //不能包含1,10,11三个售卖状态
//			QueryBuilder stats = QueryBuilders.termsQuery("sellStatus", new int[] {1,10,11});
//			yhsy.mustNot(stats);
            yhsy.must(QueryBuilders.wildcardQuery("type", "*" + BuildingGroupEnum.bestSellerBuild.getCode() + "*"));
            metaQuery.must(yhsy);
        }
    }

    /**
     * <p> 单独走关键字查询时，不分页，不加其他条件
     *
     * @param metaQuery
     * @param condition
     * @return
     */
    private Object handleKeyword(BoolQueryBuilder metaQuery, BuildingSearchConditionBean condition) {

        metaQuery.must(QueryBuilders.matchPhraseQuery("name", condition.getKeyword()));
        String[] fields = getFileds(condition.isHighLight());
        //按照开盘时间最近的降序排列
        NestedSortBuilder nsb = new NestedSortBuilder("kps");
        SortBuilder s = SortBuilders.fieldSort("kps.preSellTime").setNestedSort(nsb)
                .order(SortOrder.DESC);
        //执行查询
        Map<String, Object> data = this.doSearchBuilding(metaQuery, 1, s, fields, condition.isHighLight());
        List<Map<String, Object>> res = (List<Map<String, Object>>) data.get("data");
        if (condition.isHighLight()) {
            return ResultUtil.ok(res);
        }
        //如果不是高亮，则要处理数据再放列表页
        long total = (long) data.get("total");
        List<BuildingSearchDocumentBean> rex = new ArrayList<>();
        res.forEach(d -> {
            //将map转换为对象进行数据处理
            rex.add(JSON.parseObject(JSON.toJSONString(d), BuildingSearchDocumentBean.class));
        });
        Map<String, Object> m = new HashMap<>();
        m.put("total", total);
        //处理数据并封装返回
        m.put("rows", handleResultData(rex));
        return ResultUtil.ok(m);
    }

    /**
     * 处理列表结果数据
     */
    public static List<BuildingListDataBean> handleResultData(List<BuildingSearchDocumentBean> rex) {

        List<BuildingListDataBean> data = new ArrayList<>();
        DecimalFormat def = new DecimalFormat(".#");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (BuildingSearchDocumentBean db : rex) {
            BuildingListDataBean ld = new BuildingListDataBean();
            ld.setComments(String.valueOf(db.getComments() + 1));
            ld.setId(db.getId());
            ld.setType(db.getType());
            ld.setName(db.getName());
            ld.setPrice(db.getShowPrice() == null ? "价格待定" : db.getShowPrice());
            ld.setRegion(db.getRegionName());
            SellStateEnum stateEnum = SellStateEnum.getStateById(db.getSellStatus());
            ld.setSellStatus(stateEnum == null ? "" : stateEnum.getName());
            ld.setTags(getShowTags(db.getTags()));
            float vf = Float.valueOf(db.getVisits()) / 10000f;
            ld.setVisits(def.format(vf) + "w");
            ld.setTime(getShowTime(db, sdf));
            ld.setCoverImg(db.getCoverImg());
            if (ld.getCoverImg() == null) {
                ld.setCoverImg(deaultCoverImg);
            }
            data.add(ld);
        }
        return data;
    }

    /**
     * 展示时间判断并展示
     *
     * @param status
     * @param d
     * @return
     */
    public static String getShowTime(BuildingSearchDocumentBean d, SimpleDateFormat sdf) {

        try {
            if (d.getSellStatus().intValue() == 2)
//				return  d.getPlanKaiPan()==null?null:"预计开盘:"+d.getPlanKaiPan();
                return StringUtils.isEmpty(d.getPlanKaiPan()) ? null : "预计开盘:" + d.getPlanKaiPan();
            if (d.getSellStatus() == 3 || d.getSellStatus() == 4) {
                if (d.getKps() == null || d.getKps().size() == 0)
                    return null;
                Long x = d.getKps().get(0).getEndRegisger();
                if (x == null || x == 0)
                    return null;
                return "截止时间:" + sdf.format(x);
            }
            if (d.getSellStatus() == 5) {
                if (d.getKps() == null || d.getKps().size() == 0)
                    return null;
                Long x = d.getKps().get(0).getYaohao();
                if (x == null || x == 0)
                    return null;
                return "摇号时间:" + sdf.format(x);
            }
            if (d.getSellStatus() == 6 || d.getSellStatus() == 7) {
                if (d.getKps() == null || d.getKps().size() == 0)
                    return null;
                Long x = d.getKps().get(0).getStartSelect();
                if (x == null || x == 0)
                    return null;
                return "选房时间:" + sdf.format(x);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("楼盘检索格式化时间出错", e);
        }

        return null;
    }


    public static String[] getShowTags(List<TagSearchBean> tags) {
        if (tags == null)
            return new String[0];
        List<String> ts = new ArrayList<>();
        //查看是否有标签设置了显示的，如果设置了的话只展示设置的，否则随机展示三个
        boolean isSetShow = false;
        for (TagSearchBean tag : tags) {
            if (tag.getShowTag().intValue() == 1)
                isSetShow = true;
        }
        //只展示设置了的
        for (TagSearchBean tag : tags) {
            if (isSetShow) {
                if (tag.getShowTag().intValue() == 1) {
                    ts.add(BuildingTagEnum.getTagById(tag.getTagId()).getName());
                }
            } else {
                ts.add(BuildingTagEnum.getTagById(tag.getTagId()).getName());
            }
        }
        //如果数量超过三个要随机展示三个
        if (ts.size() > 3) {
            String[] x = getRandom3Tags(ts);
            return x;
        } else {
            String[] x = new String[ts.size()];
            for (int i = 0; i < ts.size(); i++)
                x[i] = ts.get(i);
            return x;
        }
    }

    private static String[] getRandom3Tags(List<String> tags) {
        String[] x = new String[3];

        for (int i = 0; i < 3; i++) {
            int index = 0;
            while (index <= 200) {
                index++;//死循环保护
                String t = tags.get(getRandom(tags.size()));
                boolean isRepeat = isRepeat(x, t);
                if (!isRepeat) {
                    x[i] = t;
                    break;
                }
            }
        }
        return x;
    }

    private static boolean isRepeat(String[] x, String t) {
        for (String s : x) {
            if (s == null || t == null)
                continue;
            if (s.equals(t))
                return true;
        }
        return false;
    }


    private static int getRandom(int size) {
        //随机数 0到size-1
        int min = 0;
        int max = size - 1;
        int r = new Random().nextInt(max - min) + min;
        return r;
    }


    //获取需要返回的字段
    public static String[] getFileds(boolean isHighlight) {
        if (isHighlight) {
            return new String[]{"id", "name"};
        }

        return new String[]{"id", "name", "regionName", "visits", "type"
                , "coverImg", "showPrice", "tags.tagId", "tags.showTag", "sellStatus", "comments",
                "kps.endRegisger", "kps.startSelect", "kps.yaohao", "planKaiPan", "coverImg"};
    }


    /**
     * 执行查询  多字段排序
     *
     * @param query        查询体
     * @param page         页码 如果不分页传 < 0
     * @param sort         排序
     * @param returnFileds 制定返回的字段
     * @return
     */
    @SuppressWarnings("rawtypes")
    private Map<String, Object> doSearchBuildingOrderList(QueryBuilder query, int page, List<SortBuilder> sort,
                                                          String[] returnFileds, boolean isHighlight) {

        SearchRequestBuilder req = esTemplate.getClient().prepareSearch(Constants.ES_BUILDING_INDEX_NAME)
                .setTypes(Constants.ES_TYPE_NAME_HOUSE);
        //设置请查询体
        req.setQuery(query);
        //设置排序
        if (sort.size() > 0) {
            for (SortBuilder s : sort)
                req.addSort(s);
        }


        //设置分页
        if (page > 0) {
            if (isHighlight)
                req.setFrom(0).setSize(100);
            else
                req.setFrom((page - 1) * 15).setSize(15);
        }

        //设置返回指定的字段
        req.setFetchSource(returnFileds, null);
        //设置高亮查询
        if (isHighlight)
            setHighlight(req);

        SearchResponse rep = req.execute().actionGet();
        List<Map<String, Object>> all = new ArrayList<Map<String, Object>>();
        rep.getHits().forEach(h -> {
            Map<String, Object> res = h.getSourceAsMap();
            all.add(res);
            //如果高亮了，处理高亮查询
            if (isHighlight)
                handleHigltResult(res, h);
        });

        Map<String, Object> res = new HashMap<String, Object>();
        res.put("total", rep.getHits().getTotalHits());
        res.put("data", all);
        return res;
    }

    /**
     * 执行查询
     *
     * @param query        查询体
     * @param page         页码 如果不分页传 < 0
     * @param sort         排序
     * @param returnFileds 制定返回的字段
     * @return
     */
    @SuppressWarnings("rawtypes")
    private Map<String, Object> doSearchBuilding(QueryBuilder query, int page, SortBuilder sort,
                                                 String[] returnFileds, boolean isHighlight) {

        SearchRequestBuilder req = esTemplate.getClient().prepareSearch(Constants.ES_BUILDING_INDEX_NAME)
                .setTypes(Constants.ES_TYPE_NAME_HOUSE);
        //设置请查询体
        req.setQuery(query);
        //设置排序
        req.addSort(sort);

        //设置分页
        if (page > 0) {
            if (isHighlight)
                req.setFrom(0).setSize(100);
            else
                req.setFrom((page - 1) * 15).setSize(15);
        }

        //设置返回指定的字段
        req.setFetchSource(returnFileds, null);
        //设置高亮查询
        if (isHighlight)
            setHighlight(req);

        SearchResponse rep = req.execute().actionGet();
        List<Map<String, Object>> all = new ArrayList<Map<String, Object>>();
        rep.getHits().forEach(h -> {
            Map<String, Object> res = h.getSourceAsMap();
            all.add(res);
            //如果高亮了，处理高亮查询
            if (isHighlight)
                handleHigltResult(res, h);
        });

        Map<String, Object> res = new HashMap<String, Object>();
        res.put("total", rep.getHits().getTotalHits());
        res.put("data", all);
        return res;
    }


    private void setHighlight(SearchRequestBuilder req) {
        HighlightBuilder light = new HighlightBuilder();
        light.preTags("<span style='color:#fbca29'>");
        light.postTags("</span>");
        light.field("name");
        req.highlighter(light);
    }


    private void handleHigltResult(Map<String, Object> res, SearchHit hit) {
        HighlightField hs = hit.getHighlightFields().get("name");
        Text[] tx = hs.getFragments();
        for (Text t : tx) {
            res.put("name", t.toString());
        }
    }

    /**
     * <p> 标签找房检索接口
     *
     * @param tags
     * @return
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public Object tagSearch(List<BuildingTag> tags, Integer page) {

        if (page == null)
            page = -1;//-1表示不分页
        BoolQueryBuilder metaQuery = QueryBuilders.boolQuery();
        SortBuilder sort = SortBuilders.fieldSort("rank").order(SortOrder.ASC);
        //因为系统自定义标签可以多选，分割出来单独处理
        List<BuildingTag> sysTags = new ArrayList<>();
        if (tags != null) {
            for (BuildingTag tag : tags) {
                switch (tag.getType()) {
                    case 1:
                    case 2:
                        //处理系统类型的标签
                        sysTags.add(tag);
                        break;
                    case 3:
                        //处理地铁
                        handleSubwayTags(metaQuery, tag);
                        break;
                    case 4:
                        //处理区域
                        handleRegionTags(metaQuery, tag);
                        break;
                    case 5:
                        //处理单价
                        handlePerpriceTags(metaQuery, tag);
                        break;
                    case 6:
                        //处理面积
                        handleSpaceTags(metaQuery, tag);
                        break;
                    case 7:
                        //处理户型标签
                        handleModelHouse(metaQuery, tag);
                        break;
                    case 8:
                        //处理总价标签
                        handleTotalPriceTags(metaQuery, tag);
                        break;
                    case 9:
                        //处理商场标签
                        handleMarketTags(metaQuery, tag);
                        break;
                }
            }
        }
        //处理系统标签
        handleSysTags(metaQuery, sysTags);

        Map<String, Object> data = doSearchBuilding(metaQuery, page, sort, getFileds(false), false);
        //处理数据结果
        List<Map<String, Object>> res = (List<Map<String, Object>>) data.get("data");
        long total = (long) data.get("total");
        List<BuildingSearchDocumentBean> rex = new ArrayList<>();
        res.forEach(d -> {
            //将map转换为对象进行数据处理
            rex.add(JSON.parseObject(JSON.toJSONString(d), BuildingSearchDocumentBean.class));
        });
        Map<String, Object> m = new HashMap<>();
        m.put("total", total);
        //处理数据并封装返回
        m.put("rows", handleResultData(rex));
        return ResultUtil.ok(m);
    }

    private void handleMarketTags(BoolQueryBuilder metaQuery, BuildingTag tag) {
        SearchTagMarketEnum mk = SearchTagMarketEnum.getSearchTagMarketById(tag.getId());
        if (mk == null)
            return;
        int distance = mk.getDistance();
        BoolQueryBuilder mkt = QueryBuilders.boolQuery();
        QueryBuilder mk1 = QueryBuilders.termQuery("zbs.type", 2);//type=2是商场，1是地铁  周边
        QueryBuilder mk2 = QueryBuilders.rangeQuery("zbs.distance").lte(distance);
        mkt.must(mk1);
        mkt.must(mk2);
        NestedQueryBuilder nq = QueryBuilders.nestedQuery("zbs", mkt, ScoreMode.None);
        metaQuery.must(nq);
    }

    private void handleTotalPriceTags(BoolQueryBuilder metaQuery, BuildingTag tag) {
        SearchTagTotalPriceEnum s = SearchTagTotalPriceEnum.getSearchTagById(tag.getId());
        if (s == null)
            return;
        int min = s.getMin();
        int max = s.getMax();
        BoolQueryBuilder totalPrice = QueryBuilders.boolQuery();
        QueryBuilder minq = QueryBuilders.rangeQuery("kps.maxTotalPrice").gte(min);//我的最低价小于他的最高价
        QueryBuilder maxq = QueryBuilders.rangeQuery("kps.minTotalPrice").lte(max);//我的最高价大于他的最低价
        totalPrice.must(minq);
        totalPrice.must(maxq);
        NestedQueryBuilder nq = QueryBuilders.nestedQuery("kps", totalPrice, ScoreMode.None);
        metaQuery.must(nq);
    }


    private void handleModelHouse(BoolQueryBuilder metaQuery, BuildingTag tag) {
        SearchHouseModelConditionEnum h = SearchHouseModelConditionEnum.getHouseModelById(tag.getId());
        if (h == null)
            return;
        String type = h.getType();
        metaQuery.must(QueryBuilders.matchQuery("houseModel", type));
    }

    private void handleSpaceTags(BoolQueryBuilder metaQuery, BuildingTag tag) {
        SearchSpaceConditionEnum s = SearchSpaceConditionEnum.getSpaceConditionByid(tag.getId());
        if (s == null)
            return;
        float min = s.getMinSpace();
        float max = s.getMaxSpace();
        BoolQueryBuilder totalPrice = QueryBuilders.boolQuery();
        QueryBuilder minq = QueryBuilders.rangeQuery("kps.maxSpace").gte(min);//我的最低面积小于他的最高面积
        QueryBuilder maxq = QueryBuilders.rangeQuery("kps.minSpace").lte(max);//我的最高面积大于他的最低面积
        totalPrice.must(minq);
        totalPrice.must(maxq);
        NestedQueryBuilder nq = QueryBuilders.nestedQuery("kps", totalPrice, ScoreMode.None);
        metaQuery.must(nq);

    }

    private void handlePerpriceTags(BoolQueryBuilder metaQuery, BuildingTag tag) {
        SearchPerPriceConditionEnum s = SearchPerPriceConditionEnum.getSearchPerPriceConditionById(tag.getId());
        if (s == null)
            return;
        int min = s.getMin();
        int max = s.getMax();
        BoolQueryBuilder perPrice = QueryBuilders.boolQuery();
        QueryBuilder minq = QueryBuilders.rangeQuery("kps.maxPerPrice").gte(min);//我的最低价小于他的最高价
        QueryBuilder maxq = QueryBuilders.rangeQuery("kps.minPerPrice").lte(max);//我的最高价大于他的最低价
        perPrice.must(minq);
        perPrice.must(maxq);
        NestedQueryBuilder nq = QueryBuilders.nestedQuery("kps", perPrice, ScoreMode.None);
        metaQuery.must(nq);

    }

    private void handleRegionTags(BoolQueryBuilder metaQuery, BuildingTag tag) {
        QueryBuilder region = QueryBuilders.termQuery("regionId", tag.getId());
        metaQuery.must(region);
    }

    private void handleSubwayTags(BoolQueryBuilder metaQuery, BuildingTag tag) {
        BoolQueryBuilder bool = QueryBuilders.boolQuery();
        QueryBuilder sub = QueryBuilders.termQuery("zbs.lineCode", tag.getId());
        QueryBuilder range = QueryBuilders.rangeQuery("zbs.distance").lte(1000);
        bool.must(range);
        bool.must(sub);
        NestedQueryBuilder nq = QueryBuilders.nestedQuery("zbs", bool, ScoreMode.None);
        metaQuery.must(nq);
    }

    /**
     * 可以多选
     * 备注，自定义标签中，【双地铁】筛选内容需要包含【地铁房】筛选的内容，【三地铁】筛选内容需要包括【双地铁】和【地铁房】筛选的内容，
     * 【不限购】筛选的内容需要包括【商办】筛选的内容。
     * 【不限购】包含区域为成都周边楼盘及商办公寓类物业；【商办】包含商办公寓类物业；
     * 【不摇号】包含摇号剩余楼盘列表中的楼盘，以及区域为成都周边的楼盘。
     */

    private void handleSysTags(BoolQueryBuilder metaQuery, List<BuildingTag> sysTags) {

        if (sysTags.size() == 0)
            return;
        BoolQueryBuilder sysq = QueryBuilders.boolQuery();

        for (BuildingTag tag : sysTags) {
            //有包含关系的特殊处理 789
            if (tag.getId() == 7) {
                //地铁房包含了双地铁和三地铁
                QueryBuilder sub = QueryBuilders.termsQuery("tags.tagId", new int[]{7, 8, 9});
                NestedQueryBuilder nq = QueryBuilders.nestedQuery("tags", sub, ScoreMode.None);
                sysq.should(nq);
                continue;
            }
            //不限购，包括成都周边区域的和   物业类型为公寓的和商墅
            if (tag.getId() == 12) {
                BoolQueryBuilder notLimit = QueryBuilders.boolQuery();
                notLimit.should(QueryBuilders.termsQuery("regionId", getChengduZhoubianIds()));
                //QueryBuilder tagq = QueryBuilders.termsQuery("tags.tagId",new int[] {11,32});
                //NestedQueryBuilder ns = QueryBuilders.nestedQuery("tags", tagq, ScoreMode.None);
                //notLimit.should(ns);
                sysq.should(notLimit);
                continue;
            }
            //不摇号 包括摇号剩余和成都周边
            if (tag.getId() == 13) {
                BoolQueryBuilder notLimit = QueryBuilders.boolQuery();
                notLimit.should(QueryBuilders.termsQuery("regionId", getChengduZhoubianIds()));
                notLimit.should(QueryBuilders.rangeQuery("suplusHose").gte(20));
                notLimit.mustNot(QueryBuilders.termsQuery("sellStatus", new int[]{1, 10, 11}));
                sysq.should(notLimit);
                continue;
            }
            //其他的直接去查询标签id,物业类型全部打了标签
            QueryBuilder tagq = QueryBuilders.termQuery("tags.tagId", tag.getId());
            NestedQueryBuilder ns = QueryBuilders.nestedQuery("tags", tagq, ScoreMode.None);
            sysq.should(ns);
        }
        metaQuery.must(sysq);
    }


    private int[] getChengduZhoubianIds() {
        int[] zbs = new int[]{147218, 147222, 147237, 147246, 147247, 147248, 147249, 147259, 147260, 147261
                , 147262, 147262, 147263, 147264, 147265, 147266, 147267, 147268, 147269, 147270, 147271};
        return zbs;
    }

    /**
     * 查询我关注的楼盘
     *
     * @return
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public Map<String, Object> getMyBuildingFocus(BuildingFocusReqBean bf) {
        Integer page = bf.getPage();
        if (page == null || page < 1)
            page = 1;
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termsQuery("id", bf.getIds()));
        SortBuilder sort = SortBuilders.fieldSort("rank").order(SortOrder.ASC);
        Map<String, Object> data = this.doSearchBuilding(query, page, sort, getFileds(false), false);
        //处理数据结果
        List<Map<String, Object>> res = (List<Map<String, Object>>) data.get("data");
        long total = (long) data.get("total");
        List<BuildingSearchDocumentBean> rex = new ArrayList<>();
        res.forEach(d -> {
            //将map转换为对象进行数据处理
            rex.add(JSON.parseObject(JSON.toJSONString(d), BuildingSearchDocumentBean.class));
        });
        Map<String, Object> m = new HashMap<>();
        m.put("total", total);
        //处理数据并封装返回
        m.put("rows", handleResultData(rex));
        return m;
    }

    /**
     * 一键找房
     *
     * @param condition
     * @return
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public Object fastSearch(FastFindBuildingCondition condition) {
        Integer page = condition.getPage();
        if (page == null || page < 1)
            page = 1;
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 处理总价
        dealTotalPrice(condition, boolQuery);
        // 处理单价
        dealPerPrice(condition, boolQuery);
        // 处理区域
        dealRegion(condition, boolQuery);
        // 处理户型
        dealHouseModel(condition, boolQuery);
        //处理面积
        dealArea(condition, boolQuery);
        //处理物业
        dealPropType(condition, boolQuery);
        SortBuilder sort = SortBuilders.fieldSort("rank").order(SortOrder.ASC);
        String[] fileds = getFileds(false);
        System.out.println(JSON.toJSON(boolQuery));
        Map<String, Object> data = this.doSearchBuilding(boolQuery, page, sort, fileds, false);
        long total = (long) data.get("total");
        //处理数据结果
        List<Map<String, Object>> res = (List<Map<String, Object>>) data.get("data");
        List<BuildingSearchDocumentBean> rex = new ArrayList<>();
        res.forEach(d -> {
            //将map转换为对象进行数据处理
            rex.add(JSON.parseObject(JSON.toJSONString(d), BuildingSearchDocumentBean.class));
        });
        Map<String, Object> m = new HashMap<>();
        m.put("total", total);
        //处理数据并封装返回
        m.put("rows", handleResultData(rex));
        return ResultUtil.ok(m);
    }

    private void dealPropType(FastFindBuildingCondition condition, BoolQueryBuilder boolQuery) {
        String pidStr = condition.getPropTypeIds();
        if (StringUtils.isEmpty(pidStr))
            return;
        String[] pidStrs = pidStr.split(",");
        String[] pids = new String[pidStrs.length];
        try {
            for (int i = 0; i < pidStrs.length; i++) {
                pids[i] = SearchPropTypeConditionEnum.getSearchPropTypeById(Integer.valueOf(pidStrs[i])).getType().toLowerCase();
            }
        } catch (Exception e) {
            log.error("处理物业id转换错误", e);
        }
        boolQuery.must(QueryBuilders.termsQuery("propTypes", pids));
    }

    private void dealArea(FastFindBuildingCondition condition, BoolQueryBuilder boolQuery) {
        Integer spaceId = condition.getSpaceId();
        if (spaceId == null)
            return;
        SearchSpaceConditionEnum area = SearchSpaceConditionEnum.getSpaceConditionByid(spaceId);
        if (area == null)
            return;
        float max = area.getMaxSpace();
        float min = area.getMinSpace();
        BoolQueryBuilder space = QueryBuilders.boolQuery();
        QueryBuilder spmax = QueryBuilders.rangeQuery("kps.minSpace").lte(max);
        QueryBuilder spmin = QueryBuilders.rangeQuery("kps.maxSpace").gte(min);
        NestedQueryBuilder nested = QueryBuilders.nestedQuery("kps", space, ScoreMode.None);
        space.must(spmax);
        space.must(spmin);
        boolQuery.must(nested);
    }

    private void dealHouseModel(FastFindBuildingCondition condition, BoolQueryBuilder boolQuery) {
        String modelIdStr = condition.getHouseModelIds();
        if (StringUtils.isEmpty(modelIdStr))
            return;
        String[] mods = modelIdStr.split(",");
        String[] models = new String[mods.length];
        try {
            for (int i = 0; i < mods.length; i++) {
                models[i] = SearchHouseModelConditionEnum.getHouseModelById(Integer.parseInt(mods[i])).getType().toLowerCase();
            }
        } catch (Exception e) {
            log.error("处理户型id转换错误", e);
        }
        boolQuery.must(QueryBuilders.termsQuery("houseModel", models));
    }

    private void dealRegion(FastFindBuildingCondition condition, BoolQueryBuilder boolQuery) {
        String regStr = condition.getRegionIds();
        if (StringUtils.isEmpty(regStr))
            return;
        String[] regions = regStr.split(",");
        int[] regs = new int[regions.length];
        try {
            for (int i = 0; i < regions.length; i++) {
                regs[i] = Integer.valueOf(regions[i]);
            }
        } catch (Exception e) {
            log.error("处理区域id转换错误", e);
        }
        boolQuery.must(QueryBuilders.termsQuery("regionId", regs));
    }

    private void dealPerPrice(FastFindBuildingCondition condition, BoolQueryBuilder boolQuery) {
        BoolQueryBuilder perPrice = QueryBuilders.boolQuery();
        boolean isDeal = false;
        //输入优先
        if (condition.getMinPerPrice() != null && condition.getMinPerPrice() > 0) {
            //最低价大于等于开盘单价最低价
            QueryBuilder ppmin = QueryBuilders.rangeQuery("kps.maxPerPrice").gte(condition.getMinPerPrice());
            perPrice.must(ppmin);
            isDeal = true;
        }
        if (condition.getMaxPerPrice() != null && condition.getMaxPerPrice() > 0) {
            //最高价大于等于开盘单价最低价
            QueryBuilder ppmax = QueryBuilders.rangeQuery("kps.minPerPrice").lte(condition.getMaxPerPrice());
            perPrice.must(ppmax);
            isDeal = true;
        }
        if (!isDeal) {
            Integer pid = condition.getPerPriceId();
            if (pid == null || pid == -1)
                return;
            SearchPerPriceConditionEnum perp = SearchPerPriceConditionEnum.getSearchPerPriceConditionById(pid);
            if (perp == null)
                return;
            int max = perp.getMax();
            int min = perp.getMin();
            QueryBuilder ppmax = QueryBuilders.rangeQuery("kps.minPerPrice").lte(max);
            QueryBuilder ppmin = QueryBuilders.rangeQuery("kps.maxPerPrice").gte(min);
            perPrice.must(ppmax);
            perPrice.must(ppmin);
            isDeal = true;
        }
        if (isDeal) {
            QueryBuilder nested = QueryBuilders.nestedQuery("kps", perPrice, ScoreMode.None);
            boolQuery.must(nested);
        }
    }

    private void dealTotalPrice(FastFindBuildingCondition condition, BoolQueryBuilder boolQuery) {
        BoolQueryBuilder totalPrice = QueryBuilders.boolQuery();
        boolean isHandled = false;
        //输入优先
        if (condition.getMinTotalPrice() != null && condition.getMinTotalPrice() > 0) {
            //最低价大于等于开盘总价最低价
            QueryBuilder tpmin = QueryBuilders.rangeQuery("kps.maxTotalPrice").gte(condition.getMinTotalPrice());
            totalPrice.must(tpmin);
            isHandled = true;
        }
        if (condition.getMaxTotalPrice() != null && condition.getMaxTotalPrice() > 0) {
            //最高价也是大于等于开盘总价最低价
            QueryBuilder tpmax = QueryBuilders.rangeQuery("kps.minTotalPrice").lte(condition.getMinTotalPrice());
            totalPrice.must(tpmax);
            isHandled = true;
        }
        if (!isHandled) {
            //处理选择的
            if (condition.getTotalPriceId() != null && condition.getTotalPriceId() != -1) {
                SearchTotalPriceConditionEnum total = SearchTotalPriceConditionEnum
                        .getSearchTotalPriceConditionById(condition.getTotalPriceId());
                if (total != null) {
                    //最高价也是大于等于开盘总价最低价
                    int max = total.getMax();
                    int min = total.getMin();
                    QueryBuilder tpmax = QueryBuilders.rangeQuery("kps.minTotalPrice").lte(max);
                    QueryBuilder tpmin = QueryBuilders.rangeQuery("kps.maxTotalPrice").gte(min);
                    totalPrice.must(tpmax);
                    totalPrice.must(tpmin);
                    isHandled = true;
                }
            }
        }
        //只有处理了才加入查询体
        if (isHandled) {
            QueryBuilder nested = QueryBuilders.nestedQuery("kps", totalPrice, ScoreMode.None);
            boolQuery.must(nested);
        }
    }

    /**
     * <p>半径找房，按照距离和坐标点查询es
     *
     * @param info
     * @return
     */
    @SuppressWarnings("rawtypes")
    public Object banJingZhaoFang(@RequestBody DistaceSearchInfo info) {

        if (info.getDistance() == null)
            info.setDistance(5);
        if (info.getLat() == null)
            return ResultUtil.fail("指定位置不对");
        if (info.getLng() == null)
            return ResultUtil.fail("指定位置不对");

        BoolQueryBuilder meta = QueryBuilders.boolQuery();
        meta.must(QueryBuilders.matchAllQuery());//查询所有
        //设置过滤器过滤坐标，按照距离排序
        GeoDistanceQueryBuilder gdq = new GeoDistanceQueryBuilder("location");
        gdq.point(info.getLat(), info.getLng());//设置坐标
        gdq.distance(Double.valueOf(info.getDistance()), DistanceUnit.KILOMETERS);//设置距离和单位
        meta.filter(gdq);//加入过滤器
        //设置距离排序
        SortBuilder sort = SortBuilders.geoDistanceSort("location", info.getLat(), info.getLng());
        List<Map<String, Object>> all = doDistanceSearch(meta, sort);
        List<DistanceSearchResult> res = new ArrayList<>();
        //处理搜索结果
        all.forEach(o -> {
            res.add(JSON.parseObject(JSON.toJSONString(o), DistanceSearchResult.class));
        });
        //处理售卖状态和价格
        res.forEach(r -> {
            if (r.getSellStatus() != null) {
                SellStateEnum s = SellStateEnum.getStateById(r.getSellStatus());
                r.setSellStatus_(s != null ? s.getName() : "待售");
            }
            if (StringUtils.isEmpty(r.getShowPrice()))
                r.setShowPrice_(new String[]{"价格待定"});
            else {
                r.setShowPrice_(r.getShowPrice().split("\\+"));
            }
            //设置完后清空原值
            r.setSellStatus(null);
            r.setShowPrice(null);
        });
        return ResultUtil.ok(res);
    }


    @SuppressWarnings("rawtypes")
    private List<Map<String, Object>> doDistanceSearch(QueryBuilder meta, SortBuilder sort) {
        long st = System.currentTimeMillis();
        SearchRequestBuilder req = esTemplate.getClient().prepareSearch(Constants.ES_BUILDING_INDEX_NAME)
                .setTypes(Constants.ES_TYPE_NAME_HOUSE);
        //设置请查询体
        req.setQuery(meta);
        //设置排序
        req.addSort(sort);
        //设置分页
        req.setSize(500);
        //设置返回指定的字段
        req.setFetchSource(new String[]{"id", "name", "regionName", "showPrice", "sellStatus", "location"}, null);

        SearchResponse rep = req.execute().actionGet();
        List<Map<String, Object>> all = new ArrayList<Map<String, Object>>();
        rep.getHits().forEach(h -> {
            Map<String, Object> res = h.getSourceAsMap();
            all.add(res);
            Object[] dis = h.getSortValues();
            //处理排序距离值
            Double d = (Double) dis[0];
            res.put("distance", d);
        });
        long ed = System.currentTimeMillis();
        log.info("半径距离查询结果耗时:" + (ed - st) + "ms!");
        return all;
    }

    /**
     * 优惠楼盘列表
     *
     * @param condition
     * @return
     */
    public Object discountSearch(BuildingSearchConditionBean condition) {
        System.out.println(JSON.toJSONString(condition));
        if (condition.getPage() == null)
            condition.setPage(1);
        List<BuildingDiscount> dcs = cacheService.getDiscountBuilding();
        if (dcs == null) {
            dcs = detailMapper.getDiscount(this.getDate());
            cacheService.setDiscountBuilding(dcs);
            if (dcs == null || dcs.size() < 1)
                return ResultUtil.ok(new ArrayList<BuildingListDataBean>());
        }
        BoolQueryBuilder metaQuery = QueryBuilders.boolQuery();
        List<Integer> ids = new ArrayList<>();
        dcs.forEach(d -> ids.add(d.getBuildingId()));
        metaQuery.must(QueryBuilders.termsQuery("id", ids));
        //处理关键字输入查询
        if (!StringUtils.isEmpty(condition.getKeyword()))
            return handleKeyword(metaQuery, condition);
        //处理普通筛选查询，则不处理高光
        condition.setHighLight(false);

        //处理5大类分类查询
        handle5Type(condition, metaQuery);
        //处理区域
        handleRegion(condition, metaQuery);
        //处理地铁
        handleSubway(condition, metaQuery);
        //处理总价
        handleTotalPrice(condition, metaQuery);
        //处理单价
        handlePerPrice(condition, metaQuery);
        //处理面积
        handleSpace(condition, metaQuery);
        //处理户型
        handleHouseModel(condition, metaQuery);
        //处理物业类型
        handlePropType(condition, metaQuery);
        //处理装修
        handleDecoType(condition, metaQuery);
        //处理售卖状态
        handleSellStatus(condition, metaQuery);
        //处理排序
        List<SortBuilder> sort = handleSort(condition);
        //获取要返回的字段
        String[] fileds = getFileds(false);
        //执行查询
        Map<String, Object> data = doSearchBuildingOrderList(metaQuery, condition.getPage(), sort, fileds, false);
        //处理数据结果
        List<Map<String, Object>> res = (List<Map<String, Object>>) data.get("data");
        long total = (long) data.get("total");
        List<BuildingSearchDocumentBean> rex = new ArrayList<>();
        res.forEach(d -> {
            //将map转换为对象进行数据处理
            rex.add(JSON.parseObject(JSON.toJSONString(d), BuildingSearchDocumentBean.class));
        });
        Map<String, Object> m = new HashMap<>();
        m.put("total", total);
        //处理数据并封装返回
        List<BuildingListDataBean> bds = handleResultData(rex);
        if (bds != null && bds.size() > 0) {
            for (BuildingListDataBean bd : bds) {
                for (BuildingDiscount dc : dcs) {
                    if (bd.getId() == dc.getBuildingId()) {
                        bd.setYhTitle(dc.getTitle());
                        break;
                    }
                }
            }
        }
        m.put("rows", bds);
        return ResultUtil.ok(m);
    }

    private String getDate() {
        Calendar c = Calendar.getInstance();
        c.add(Calendar.DATE, -45);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String time = sdf.format(c.getTime());
        return time + " 00:00:00";
    }
}
